package com.xy.service.system;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xy.api.exception.BizException;
import com.xy.api.model.dto.system.RoleDTO;
import com.xy.api.model.request.system.KeywordPageRequest;
import com.xy.api.model.response.Rows;
import com.xy.api.service.system.IRoleService;
import com.xy.mapper.system.MenuRoleMapper;
import com.xy.mapper.system.RoleMapper;
import com.xy.mapper.system.UserRoleMapper;
import com.xy.model.condition.system.RoleCondition;
import com.xy.model.po.system.MenuRolePO;
import com.xy.model.po.system.RolePO;
import com.xy.model.po.system.UserRolePO;
import com.xy.utils.CustomBeanUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RoleServiceImpl implements IRoleService {

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private MenuRoleMapper menuRoleMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Override
    public Rows<RoleDTO> queryRoleList(KeywordPageRequest request) {
        Rows<RoleDTO> rows = new Rows<>();
        RoleCondition roleCondition = new RoleCondition();
        roleCondition.setKeyword(request.getKeyword());

        int total = roleMapper.countList(roleCondition);
        if (total > 0) {
            Integer pageSize = null != request.getPageSize() ? request.getPageSize() : 10;
            Integer pageNum = null != request.getPageNum() ? request.getPageNum() : 1;
            Integer pageStart = (pageNum - 1) * pageSize;
            roleCondition.setPageSize(pageSize);
            roleCondition.setPageStart(pageStart);
            List<RolePO> rolePOS = roleMapper.queryList(roleCondition);
            List<RoleDTO> list = CustomBeanUtil.copyListProperties(rolePOS, RoleDTO::new);
            fillMenuIdLIst(list);
            rows.setRows(list);
        }
        rows.setTotal(total);
        return rows;
    }

    @Override
    public Rows<RoleDTO> queryAllRoleList() {
        Rows<RoleDTO> rows = new Rows<>();
        List<RolePO> rolePOS = roleMapper.queryList(new RoleCondition());
        List<RoleDTO> list = CustomBeanUtil.copyListProperties(rolePOS, RoleDTO::new);
        rows.setRows(list);
        rows.setTotal(list.size());
        return rows;
    }

    @Override
    public RoleDTO queryRoleDetail(Long id) {
        Assert.notNull(id, "id必填");
        RolePO rolePO = roleMapper.selectById(id);
        RoleDTO role = new RoleDTO();
        CustomBeanUtil.copyProperties(rolePO, role);

        fillMenuIdList(role);
        return role;
    }

    /**
     * 填充角色关联的菜单
     *
     * @param list
     */
    private void fillMenuIdLIst(List<RoleDTO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<String> roleCodeList = list.stream().map((e -> e.getRoleCode())).collect(Collectors.toList());
        LambdaQueryWrapper<MenuRolePO> menuRoleQuery = new LambdaQueryWrapper<>();
        menuRoleQuery
                .in(MenuRolePO::getRoleCode, roleCodeList)
                .eq(MenuRolePO::getIsDeleted, false);
        List<MenuRolePO> menuRolePOS = menuRoleMapper.selectList(menuRoleQuery);
        Map<String, List<Long>> roleCodeMenuIdMap = menuRolePOS.stream()
                .collect(Collectors.groupingBy(MenuRolePO::getRoleCode, Collectors.mapping(MenuRolePO::getMenuId, Collectors.toList())));

        for (RoleDTO role : list) {
            if (roleCodeMenuIdMap.containsKey(role.getRoleCode())) {
                role.setMenuIdList(roleCodeMenuIdMap.get(role.getRoleCode()));
            }
        }
    }

    /**
     * 填充角色关联的菜单
     *
     * @param role
     */
    private void fillMenuIdList(RoleDTO role) {
        if (null == role) {
            return;
        }
        LambdaQueryWrapper<MenuRolePO> menuRoleQuery = new LambdaQueryWrapper<>();
        menuRoleQuery
                .eq(MenuRolePO::getRoleCode, role.getRoleCode())
                .eq(MenuRolePO::getIsDeleted, false);
        List<MenuRolePO> menuRolePOS = menuRoleMapper.selectList(menuRoleQuery);
        List<Long> menuIdList = menuRolePOS.stream().map(e -> e.getMenuId()).collect(Collectors.toList());
        role.setMenuIdList(menuIdList);
    }

    @Override
    public void editRole(RoleDTO request) {
        Assert.hasText(request.getRoleCode(), "角色标识必填");
        Assert.hasText(request.getRoleName(), "角色名称必填");

        LambdaQueryWrapper<RolePO> existWrapper = new LambdaQueryWrapper<>();
        existWrapper.eq(RolePO::getRoleCode, request.getRoleCode()).eq(RolePO::getIsDeleted, false);
        RolePO existRoleByCode = roleMapper.selectOne(existWrapper);
        if (null != existRoleByCode) {
            if (null == request.getId()) {
                throw new BizException("角色标识已存在");
            } else if (!Objects.equals(request.getId(), existRoleByCode.getId())) {
                throw new BizException("角色标识已存在");
            }
        }
        RolePO record = new RolePO();
        CustomBeanUtil.copyProperties(request, record);
        if (null != request.getId()) {
            roleMapper.updateById(record);
        } else {
            roleMapper.insert(record);
        }
        updateMenuRole(request);
    }

    /**
     * 角色与菜单关联关系
     *
     * @param request
     */
    @Transactional
    private void updateMenuRole(RoleDTO request) {
        MenuRolePO deleteRecord = new MenuRolePO();
        deleteRecord.setIsDeleted(true);
        LambdaQueryWrapper<MenuRolePO> deleteQuery = new LambdaQueryWrapper<>();
        deleteQuery.eq(MenuRolePO::getRoleCode, request.getRoleCode());
        menuRoleMapper.update(deleteRecord, deleteQuery);

        if (null == request.getId()) {
            //根菜单
            MenuRolePO record = new MenuRolePO();
            record.setRoleCode(request.getRoleCode());
            record.setMenuId(1l);
            menuRoleMapper.insert(record);
        }

        if (!CollectionUtils.isEmpty(request.getMenuIdList())) {
            for (Long menuId : request.getMenuIdList()) {
                MenuRolePO record = new MenuRolePO();
                record.setRoleCode(request.getRoleCode());
                record.setMenuId(menuId);
                menuRoleMapper.insert(record);
            }
        }
    }

    @Override
    @Transactional
    public void deleteRole(Long id) {
        RolePO existRole = roleMapper.selectById(id);
        if (null == existRole) {
            return;
        }
        String roleCode = existRole.getRoleCode();

        RolePO record = new RolePO();
        record.setId(id);
        record.setIsDeleted(true);
        roleMapper.updateById(record);

        MenuRolePO deleteMenuRoleRecord = new MenuRolePO();
        deleteMenuRoleRecord.setIsDeleted(true);
        LambdaQueryWrapper<MenuRolePO> deleteMenuRoleQuery = new LambdaQueryWrapper<>();
        deleteMenuRoleQuery.eq(MenuRolePO::getRoleCode, roleCode);
        menuRoleMapper.update(deleteMenuRoleRecord, deleteMenuRoleQuery);

        UserRolePO deleteUserRoleRecord = new UserRolePO();
        deleteUserRoleRecord.setIsDeleted(true);
        LambdaQueryWrapper<UserRolePO> deleteUserRoleQuery = new LambdaQueryWrapper<>();
        deleteUserRoleQuery.eq(UserRolePO::getRoleCode, roleCode);
        userRoleMapper.update(deleteUserRoleRecord, deleteUserRoleQuery);
    }
}
